package blue.http.internal.core.plugin;

import blue.base.core.cache.Cache;
import blue.base.core.cache.CacheLoader;
import blue.base.core.cache.RemovalCause;
import blue.base.core.cache.RemovalListener;
import blue.base.core.collection.MultiMap;
import blue.http.core.message.Session;
import blue.http.core.plugin.SessionProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * @author Jin Zheng
 * @since 1.0 2021-08-26
 */
public class MemorySessionProvider implements SessionProvider, CacheLoader<String, Session>,
		RemovalListener<String, Session> {
	private static Logger logger = LoggerFactory.getLogger(MemorySessionProvider.class);

	private final Duration expireDuration;
	// Map<token, Session>
	private final Cache<String, Session> sessionCache;
	// Map<key, token>
	private final MultiMap<String, String> keyCache;

	public MemorySessionProvider() {
		this(DEFAULT_EXPIRE);
	}

	public MemorySessionProvider(Duration expireDuration) {
		this.expireDuration = expireDuration;
		this.sessionCache = Cache.builder()
				.expireAfterAccess(expireDuration.toMillis(), TimeUnit.MILLISECONDS)
				.removalListener(this)
				.build(this);
		this.keyCache = MultiMap.createConcurrent();
	}

	@Override
	public Session getSession(String token) {
		if (token == null || token.isEmpty())
			return null;

		return sessionCache.get(token);
	}

	public void removeSession(String token) {
		var session = sessionCache.get(token);
		if (session != null && session.getKey() != null && !session.getKey().isEmpty()) {
			keyCache.remove(session.getKey());
		}
		sessionCache.remove(token);
		logger.info("Remove session, token: {}", token);
	}

	public void setKey(String token, String key) {
		keyCache.put(token, key);
	}

	@Override
	public Session load(String key) throws Exception {
		logger.debug("Create session, token: {}", key);
		return new MemorySession(key, this);
	}

	@Override
	public void onRemoval(String key, Session value, RemovalCause cause) {
		if (value != null && value.getKey() != null && !value.getKey().isEmpty()) {
			keyCache.remove(key, value.getKey());
			logger.debug("Remove session, token: {}, key: {}", key, value.getKey());
		}
	}
}
